﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;

namespace ALS.Component.Tools.AsyncTcp
{
    /// <summary>
    /// 异步Tcp服务器
    /// </summary>
    public class AsyncTcpServer : IDisposable
    {
        #region Fields

        private TcpListener listener;
        private bool disposed = false;

        #endregion

        #region Properties

        /// <summary>
        /// 服务器是否正在运行
        /// </summary>
        public bool IsRunning { get; private set; }

        /// <summary>
        /// 监听的IP地址
        /// </summary>
        public IPAddress Address { get { return Dns.GetHostAddresses(Dns.GetHostName()).First(a => a.AddressFamily == AddressFamily.InterNetwork); } }

        /// <summary>
        /// 监听的端口
        /// </summary>
        public int Port { get; private set; }

        /// <summary>
        /// 通信所使用的编码
        /// </summary>
        public Encoding Encoding { get { return Encoding.UTF8; } }

        /// <summary>
        /// 连接中的Tcp客户端集合
        /// </summary>
        public List<TcpClientState> TcpClients { get; private set; }

        #endregion

        #region Ctors

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="listenPort">监听的端口</param>
        public AsyncTcpServer(int port)
        {
            TcpClients = new List<TcpClientState>();
            Port = port;
            listener = new TcpListener(Address, Port);
            listener.AllowNatTraversal(true);
        }

        #endregion

        #region Events

        /// <summary>
        /// 接收到数据报文事件
        /// </summary>
        public event EventHandler<TcpDatagramReceivedEventArgs<byte[]>> DatagramReceived;

        /// <summary>
        /// 接收到数据报文明文事件
        /// </summary>
        public event EventHandler<TcpDatagramReceivedEventArgs<string>> PlaintextReceived;

        private void RaiseDatagramReceived(TcpClient sender, byte[] datagram)
        {
            if (DatagramReceived != null)
            {
                DatagramReceived(this, new TcpDatagramReceivedEventArgs<byte[]>(sender, datagram));
            }
        }

        private void RaisePlaintextReceived(TcpClient sender, byte[] datagram)
        {
            if (PlaintextReceived != null)
            {
                PlaintextReceived(this, new TcpDatagramReceivedEventArgs<string>(sender, this.Encoding.GetString(datagram, 0, datagram.Length)));
            }
        }

        public event EventHandler<TcpClientConnectedEventArgs> ClientConnected;

        public event EventHandler<TcpClientDisconnectedEventArgs> ClientDisconnected;

        private void RaiseClientConnected(TcpClient tcpClient)
        {
            if (ClientConnected != null)
            {
                ClientConnected(this, new TcpClientConnectedEventArgs(tcpClient));
            }
        }

        private void RaiseClientDisconnected(TcpClient tcpClient)
        {
            if (ClientDisconnected != null)
            {
                ClientDisconnected(this, new TcpClientDisconnectedEventArgs(tcpClient));
            }
        }

        #endregion

        #region Server

        /// <summary>
        /// 启动服务器
        /// </summary>
        /// <param name="backlog">服务器所允许的挂起连接序列的最大长度</param>
        /// <returns></returns>
        public AsyncTcpServer Start(int backlog = 0)
        {
            if (!IsRunning)
            {
                IsRunning = true;
                if (backlog == 0)
                {
                    listener.Start();
                }
                else
                {
                    listener.Start(backlog);
                }
                listener.BeginAcceptTcpClient(new AsyncCallback(HandleTcpClientAccepted), listener);
            }
            return this;
        }

        /// <summary>
        /// 停止服务器
        /// </summary>
        /// <returns></returns>
        public AsyncTcpServer Stop()
        {
            if (IsRunning)
            {
                IsRunning = false;
                listener.Stop();
                lock (this.TcpClients)
                {
                    for (var i = 0; i < TcpClients.Count; i++)
                    {
                        TcpClients[i].TcpClient.Client.Disconnect(false);
                    }
                    TcpClients.Clear();
                }
            }
            return this;
        }

        #endregion

        #region Recieve

        private void HandleTcpClientAccepted(IAsyncResult ar)
        {
            if (IsRunning)
            {
                var tcpListener = (TcpListener)ar.AsyncState;
                var tcpClient = tcpListener.EndAcceptTcpClient(ar);
                var buffer = new byte[tcpClient.ReceiveBufferSize];
                var tcpClientState = new TcpClientState(tcpClient, buffer);

                lock (TcpClients)
                {
                    TcpClients.Add(tcpClientState);
                    RaiseClientConnected(tcpClient);
                }

                var networkStream = tcpClientState.NetworkStream;
                networkStream.BeginRead(tcpClientState.Buffer, 0, tcpClientState.Buffer.Length, HandleDatagramReceived, tcpClientState);
                tcpListener.BeginAcceptTcpClient(new AsyncCallback(HandleTcpClientAccepted), ar.AsyncState);
            }
        }

        private void HandleDatagramReceived(IAsyncResult ar)
        {
            if (IsRunning)
            {
                var tcpClientState = (TcpClientState)ar.AsyncState;
                var networkStream = tcpClientState.NetworkStream;

                var numberOfReadBytes = 0;
                try
                {
                    numberOfReadBytes = networkStream.EndRead(ar);
                }
                catch
                {
                    numberOfReadBytes = 0;
                }

                if (numberOfReadBytes == 0)
                {
                    // connection has been closed
                    lock (TcpClients)
                    {
                        TcpClients.Remove(tcpClientState);
                        RaiseClientDisconnected(tcpClientState.TcpClient);
                        return;
                    }
                }

                // received byte and trigger event notification
                var receivedBytes = new byte[numberOfReadBytes];
                Buffer.BlockCopy(tcpClientState.Buffer, 0, receivedBytes, 0, numberOfReadBytes);
                RaiseDatagramReceived(tcpClientState.TcpClient, receivedBytes);
                RaisePlaintextReceived(tcpClientState.TcpClient, receivedBytes);

                // continue listening for tcp datagram packets
                networkStream.BeginRead(tcpClientState.Buffer, 0, tcpClientState.Buffer.Length, HandleDatagramReceived, tcpClientState);
            }
        }

        #endregion

        #region Send

        /// <summary>
        /// 发送报文至指定的客户端
        /// </summary>
        /// <param name="tcpClient">客户端</param>
        /// <param name="datagram">报文</param>
        public void Send(TcpClient tcpClient, byte[] datagram)
        {
            if (!IsRunning)
            {
                throw new InvalidProgramException("This TCP server has not been started.");
            }

            if (tcpClient == null)
            {
                throw new ArgumentNullException("tcpClient");
            }

            if (datagram == null)
            {
                throw new ArgumentNullException("datagram");
            }

            tcpClient.GetStream().BeginWrite(datagram, 0, datagram.Length, HandleDatagramWritten, tcpClient);
        }

        private void HandleDatagramWritten(IAsyncResult ar)
        {
            ((TcpClient)ar.AsyncState).GetStream().EndWrite(ar);
        }

        /// <summary>
        /// 发送报文至指定的客户端
        /// </summary>
        /// <param name="tcpClient">客户端</param>
        /// <param name="datagram">报文</param>
        public void Send(TcpClient tcpClient, string datagram)
        {
            Send(tcpClient, Encoding.GetBytes(datagram));
        }

        /// <summary>
        /// 发送报文至所有客户端
        /// </summary>
        /// <param name="datagram">报文</param>
        public void SendAll(byte[] datagram)
        {
            if (!IsRunning)
            {
                throw new InvalidProgramException("This TCP server has not been started.");
            }

            for (int i = 0; i < TcpClients.Count; i++)
            {
                Send(TcpClients[i].TcpClient, datagram);
            }
        }

        /// <summary>
        /// 发送报文至所有客户端
        /// </summary>
        /// <param name="datagram">报文</param>
        public void SendAll(string datagram)
        {
            if (!IsRunning)
            {
                throw new InvalidProgramException("This TCP server has not been started.");
            }

            SendAll(Encoding.GetBytes(datagram));
        }

        #endregion

        #region IDisposable Members

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (!this.disposed)
            {
                if (disposing)
                {
                    try
                    {
                        Stop();
                        if (listener != null)
                        {
                            listener = null;
                        }
                    }
                    catch (SocketException ex)
                    {
                        throw ex;
                        //ExceptionHandler.Handle(ex);
                    }
                }
                disposed = true;
            }
        }

        #endregion
    }
}
